<?php

/**
 * 
 * Example :
 * /*$result = Mac_Db_Query_Select::fields('a')
 *              ->from(Mac_Db_Clause_From::tables(array(
 *              	'table1',
 *              	Db_Clause_Join::create('table2', Db_Type_Join::NATURAL_JOIN, 'table3', 
 *              	    Mac_Db_Clause_Where::clauses('truc', Mac_Db_Type_Op::BETWEEN, array(2, 18))
 *             	))))
 *              ->where(
 *                  Mac_Db_Clause_Where::clauses('b', Mac_Db_Type_Op::IS_NULL)
 *                 ->andWhere('c', Mac_Db_Type_Op::EQUALS, "ro'ger"))
 *              ->orWhere(
 *                  Mac_Db_Clause_Where::clauses('a', Mac_Db_Type_Op::NOT_EQUAL, 48)
 *              )
 *               ->execute();
 * @author Sokaru
 *
 */

class Mac_Db_Query_Select extends Mac_Db_Query_Abstract
{
    private $selectedFields = array();
    
    private $fromClause = null;
    
    private $whereClause = array();
    
    private $options = array();
    
    private function __construct (array $fields)
    {
        $this->selectedFields = $fields;
    }
    
    /**
     * Initialize the query
     */
    public static function fields ($fields = null)
    {
        if (!is_array($fields)) {
            $fields = array($fields);
        }
        return new self($fields);
    }
    
    public static function withId ($fields, $table, $id_field, $id_value)
    {
        return self::fields($fields)
            ->from($table)
            ->where(Mac_Db_Clause_Where::clauses($id_field, Mac_Db_Type_Op::EQUALS, $id_value));
    }
    
    public function addField ($field)
    {
        $this->selectedFields[] = $field;
        return $this;
    }
    
    public function addFields (array $fields)
    {
        $this->selectedFields = array_merge($this->selectedFields, $fields);
        return $this;
    }
    
    public function from(Mac_Db_Clause_From $from) 
    {
        $this->fromClause = $from;
        return $this;
    }
    
    public function where (Mac_Db_Clause_Where $where)
    {
        $this->whereClause = array($where->__toString());
        return $this;
    }
    
    public function orWhere (Mac_Db_Clause_Where $where)
    {
        $this->whereClause[] = $where->__toString();
        return $this;
    }
    
    public function limit ($limit)
    {
        $this->options['limit'] = $limit;
        return $this;
    }
    
    public function offset ($offset)
    {
        $this->options['offset'] = $offset;
        return $this;
    }
    
    public function groupBy ($fields, $having = null)
    {
        $this->options['group'] = $fields;
        if ($having !== null) {
            $this->options['having'] = $having;
        }
        return $this;
    }
    
    public function orderBy ($order)
    {
        $this->options['order'] = $order;
        return $this;
    }
    
    public function setCaching ($isActive)
    {
        $this->options['cache'] = (bool)$isActive;
        return $this;
    }
     
    private function outputSelectedFields ()
    {
        if ($this->selectedFields === null) {
            $this->selectedFields = array('*');
        }
        $fields = array();
        foreach ($this->selectedFields as $key => $name) {
            if (!is_numeric($key)) {
                $fields[] = $name.' AS '.$key;
            } else {
                $fields[] = $name;
            }
        }
        return implode(', ', $fields);
    }
    
    private function outputAsComaList ($param)
    {
        if (is_array($param)) {
            return implode(', ', $param);
        } else {
            return (string)$param;
        }
    }
    
    /**
     * (non-PHPdoc)
     * @see Db_Query_Abstact::__toString()
     */
    public function __toString ()
    {
        $req = "SELECT ";
        
        if (isset($this->options['cache'])) {
            if ($this->options['cache']===true) {
                $req .= " SQL_CACHE ";
            } elseif ($this->options['cache']===false) {
                $req .= " SQL_NO_CACHE ";
            }
        }
        
        $req .= $this->outputSelectedFields();
        $req .= " FROM ";
        $req .= $this->fromClause->__toString();
        
        if (!empty($this->whereClause)) {
            $req .= " WHERE ".Mac_Db_Helper::outputWhere($this->whereClause);
        }
        
        if (isset($this->options['group'])) {
            $req .= " GROUP BY ".$this->outputAsComaList($this->options['group']);
            if (isset($this->options['having'])) {
                $req .= ' HAVING '.$this->options['having']->__toString();
            }
        }
        
        if (isset($this->options['order'])) {
            $req .= " ORDER BY ".Mac_Db_Helper::outputOrderBy($this->options['order']);
        }
        
        // param 'limit' (with or without param 'offset')
        if ( (isset($this->options['limit'])) && (ctype_digit((string)$this->options['limit'])) ) {
            if ( (isset($this->options['offset'])) && (ctype_digit((string)$this->options['offset'])) ) {
                $offset = $this->options['offset'];
            } else {
                $offset = 0;
            }
            $req .= " LIMIT $offset, ".$this->options['limit'];
        }
        // param 'offset' without 'limit' param
        elseif ( (isset($this->options['offset'])) && (ctype_digit((string)$this->options['offset'])) ) {
            $req .= " LIMIT ".$this->options['offset'].", 18446744073709551615";
        }
        
        return $req;
    }
}